package edu.nf.llmtest.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.nf.llmtest.common.converter.EssayDTOConverter;
import edu.nf.llmtest.common.enumeration.RedisKeyEnum;
import edu.nf.llmtest.mapper.EssayMapper;
import edu.nf.llmtest.model.dto.EssayDTO;
import edu.nf.llmtest.service.EssayService;
import edu.nf.llmtest.service.impl.JinaEmbeddingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.http.HttpStatus;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.*;

/**
 * @ Author bin
 * @ Create  2025/11/4 15:57
 * 向量数据库服务
 **/
@Service
@Slf4j
public class QdrantVectorService {
    private final WebClient webClient;

    /** 当前操作的集合名称（Collection Name） */
    private final String collection;

    @Autowired
    private ObjectMapper mapper;

    @Autowired
    private JinaEmbeddingService jinaEmbeddingService;

    @Autowired
    private EssayService essayService;


    /**
     * 构造函数
     *
     * @param baseUrl    Qdrant 服务地址，
     * @param collection 默认集合名称
     */
    public QdrantVectorService(@Value("${qdrant.base-url}") String baseUrl,
                               @Value("${qdrant.collection}") String collection) {
        this.webClient = WebClient.builder().baseUrl(baseUrl).build();
        this.collection = collection;
    }

    /**
     * 同步数据到向量库
     * */
    public void SyncEssayQdrant(){
        List<EssayDTO> essayDTOS = essayService.listEssay();

        for (EssayDTO essayDTO : essayDTOS) {
            //生成向量,这里把文章内容生成向量
            List<Float> vector = jinaEmbeddingService.embedText(essayDTO.getTitle()).block();
            // 使用 Jackson 解析结果
            try {
                upsertPoint(
                    essayDTO.getEId(),//存入Qdrant的ID
                    vector,//向量数据
                    mapper.writeValueAsString(essayDTO))//元数据
                    .block(); // 在非响应式上下文中阻塞等待结果
            } catch (Exception e) {
                log.error("向Qdrant写入数据失败: {}", e.getMessage());
            }
        }
    }
    public void insertEssayVector(EssayDTO essay) {
        //生成向量,这里把文章内容生成向量
        List<Float> vector = jinaEmbeddingService.embedText(essay.getTitle()).block();
        try {
            upsertPoint(
                    essay.getEId(),//存入Qdrant的ID
                    vector,//向量数据
                    mapper.writeValueAsString(essay))//元数据
                    .block(); // 在非响应式上下文中阻塞等待结果
        } catch (Exception e) {
            log.error("向Qdrant写入数据失败: {}", e.getMessage());
        }

    }


    /**
     * 插入一个向量（测试用）
     *
     * @param vector 向量数据（浮点列表）
     * @return 插入状态字符串
     */
    public Mono<String> insertVector(List<Float> vector) {
        return upsertPoint(1, vector, "这是一个测试文档")
                .map(response -> "Vector inserted.");
    }

    /**
     * 搜索与输入向量最相似的结果（响应式版本）
     *
     * @param vector 查询向量
     * @return Mono包装的搜索结果
     */
    public Mono<List<Map<String, Object>>> search(List<Float> vector)  {
        return search(vector, 3);
    }

    /**
     * 创建集合（Collection）- 响应式版本
     *
     * @param vectorSize 向量维度大小（如 768、1024）
     * @return Mono<String> 创建结果
     */
    public Mono<String> createCollection(int vectorSize) {
        Map<String, Object> body = Map.of(
                "vectors", Map.of("size", vectorSize, "distance", "Cosine"),
                "optimizers_config", Map.of()
        );

        return webClient.put()
                .uri("/collections/" + collection)
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .exchangeToMono(clientResponse -> {
                    if (clientResponse.statusCode().is2xxSuccessful()) {
                        return clientResponse.bodyToMono(String.class);
                    }
                    if (clientResponse.statusCode().value() == 409) { // already exists
                        return clientResponse.bodyToMono(String.class)
                                .defaultIfEmpty("Collection already exists");
                    }
                    return clientResponse.createException().flatMap(reactor.core.publisher.Mono::error);
                })
                .doOnNext(response -> System.out.println("✅ Collection ensured: " + collection + " -> " + response));
    }

    /**
     * 删除当前集合 - 响应式版本
     * @return Mono<String> 删除结果
     */
    public Mono<String> deleteCollection() {
        return webClient.delete()
                .uri("/collections/" + collection)
                .exchangeToMono(clientResponse -> {
                    if (clientResponse.statusCode().is2xxSuccessful()) {
                        return clientResponse.bodyToMono(String.class);
                    }
                    if (clientResponse.statusCode().value() == 404) { // not found
                        return reactor.core.publisher.Mono.just("Collection not found");
                    }
                    return clientResponse.createException().flatMap(reactor.core.publisher.Mono::error);
                })
                .doOnNext(response -> System.out.println("🗑️  Collection delete called: " + collection + " -> " + response));
    }



    /**
     * 根据点 ID 删除数据点 - 响应式版本
     * @param pointId 点的唯一 ID
     * @return Mono<String> 删除结果
     */
    public Mono<String> deletePointById(Integer pointId) {
        log.info("删除点：{}", pointId);
        Map<String, Object> body = Map.of("points", List.of(pointId));

        return webClient.post()
                .uri("/collections/" + collection + "/points/delete")
                .bodyValue(body)
                .retrieve()
                .bodyToMono(String.class)
                .doOnNext(response -> System.out.println("🗑️  Point deleted: " + pointId));  // 这里可能没执行
    }


    /**
     * 通过id查询
     * */
    public Mono<Map<String, Object>> getPointById(Integer pointId) {
        return webClient.get()
                .uri("/collections/" + collection + "/points/" + pointId)
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})  // 使用 ParameterizedTypeReference
                .doOnNext(point -> System.out.println("🔍 Point found: " + pointId));
    }


    /**
     * 插入或更新一个向量数据点（Point）- 响应式版本
     *
     * @param id 点的唯一ID
     * @param vector 向量值（浮点列表）
     * @param text   对应文本内容或元信息（payload）
     * @return Mono<String> 插入结果
     */
    public Mono<String> upsertPoint(Integer id, List<Float> vector, String text) {
        return Mono.fromCallable(() -> {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> payload = Map.of("text", mapper.readTree(text));
            Map<String, Object> point = Map.of(
                    "id", id,
                    "vector", vector,
                    "payload", payload
            );
            return Map.of("points", List.of(point));
        }).flatMap(body -> webClient.put()
                .uri("/collections/" + collection + "/points")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .bodyToMono(String.class)
                .doOnNext(response -> System.out.println("✅ Vector inserted into Qdrant."))
        );
    }

    /**
     * 搜索与指定向量最相似的点（Point）- 响应式版本
     * @param vector 向量数据（浮点列表）
     * @param topK   返回前 K 条相似结果
     * @return Mono包装的搜索结果
     */
    public Mono<List<Map<String, Object>>> search(List<Float> vector, int topK) {
        log.info("开始向量搜索，向量维度: {}, topK: {}", vector.size(), topK);
        // 修复：Qdrant API 要求 score_threshold 在 params 中，或者使用更简单的格式
        Map<String, Object> body = Map.of(
                "vector", vector,
                "limit", topK,
                "score_threshold",0.5,
                "with_payload", true
        );

        return webClient.post()
                .uri("/collections/" + collection + "/points/search")
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(body)
                .retrieve()
                .bodyToMono(String.class)
                .doOnError(e -> log.error("向量搜索请求失败，请求体: {}, 错误: {}", body, e.getMessage()))
                .map(result -> {
                    try {
                        // 使用 Jackson 解析结果
                        ObjectMapper mapper = new ObjectMapper();
                        JsonNode root = mapper.readTree(result);

                        List<Map<String, Object>> results = new ArrayList<>();
                        for (JsonNode item : root.path("result")) {
                            Map<String, Object> record = new HashMap<>();
                            record.put("id", item.path("id").asText());
                            record.put("score", item.path("score").asDouble());
                            record.put("payload", mapper.convertValue(item.path("payload"), Map.class));
                            results.add(record);
                        }

                        log.info("向量数据库查询成功，返回 {} 条结果", results.size());
                        return results;
                    } catch (Exception e) {
                        log.error("解析向量搜索结果失败: {}", e.getMessage(), e);
                        return new ArrayList<>();
                    }
                });
    }


    /**
     * 通过关键字查询相关文章（完全响应式版本）
     * @param text 查询关键字
     * @return Mono包装的文章列表
     * */
    public Mono<List<EssayDTO>> SearchByText(String text){
        // 使用响应式链式调用：embedText -> search -> 转换为 EssayDTO
        return jinaEmbeddingService.embedText(text)
                .flatMap(floats -> search(floats)
                        .map(results -> {
                            List<EssayDTO> essayDTOS = new ArrayList<>();
                            for (Map<String, Object> item : results) {
                                Map<String, Object> payload = (Map<String, Object>) item.get("payload");
                                Map<String, Object> textMap = (Map<String, Object>) payload.get("text");
                                essayDTOS.add(mapper.convertValue(textMap, EssayDTO.class));
                            }
                            return essayDTOS;
                        })
                )
                .onErrorResume(e -> {
                    log.error("向量搜索失败: {}", e.getMessage(), e);
                    return Mono.just(new ArrayList<>());
                });
    }

    public static Map<String, Object> functionSchema() {
        return Map.of(
                "name", "query_qdrant",
                "description", "通过关键字向量化，在向量数据库检索相似的文章",
                "parameters", Map.of(
                        "type", "object",
                        "properties", Map.of(
                                "text", Map.of(
                                        "type", "string",
                                        "description", "用于向量数据库检索的关键字"
                                )
                        ),
                        "required", List.of("text")
                )
        );
    }

}
